#include "stdafx.h"
#include "QOpenglView.h"
#include "fstream"
#include "sstream"
#include <QOpenGLShaderProgram>
#include <QKeyEvent>

void QOpenglMeshObj::LoadGeneralObj(const QVector<QVector3D>& vPnt, const QVector<QVector3D>& vNormal, const QVector<QVector3D>& vColor, const QVector<int>& vInx, int iTypeDraw)
{
#ifdef DEBUG
	m_vPnt = vPnt;
#endif
	m_iPntSize = vPnt.size();
	m_iInxSize = vInx.size();
	m_bufferVertData.create();
	m_bufferVertData.bind();
	m_bufferVertData.allocate(vPnt.size() * sizeof(QVector3D) * 3);

	int iOffset = 0;
	m_bufferVertData.write(iOffset, vPnt.data(), vPnt.size() * sizeof(QVector3D));
	iOffset += vPnt.size() * sizeof(QVector3D);

	if (!vNormal.isEmpty())
	{
		m_bufferVertData.write(iOffset, vNormal.data(), vNormal.size() * sizeof(QVector3D));
	}
	else
	{
		QVector<QVector3D> vN(vPnt.size(), QVector3D(0, 1, 0));
		m_bufferVertData.write(iOffset, vN.data(), vN.size() * sizeof(QVector3D));
	}
	iOffset += vPnt.size() * sizeof(QVector3D);

	if (!vColor.isEmpty())
	{
		m_bufferVertData.write(iOffset, vColor.data(), vColor.size() * sizeof(QVector3D));
	}
	else
	{
		QVector<QVector3D> vC(vPnt.size(), QVector3D());
		m_bufferVertData.write(iOffset, vC.data(), vC.size() * sizeof(QVector3D));
	}
	iOffset += vPnt.size() * sizeof(QVector3D);

	m_bufferIndex.create();
	m_bufferIndex.bind();
	m_bufferIndex.allocate(vInx.data(), sizeof(int)*vInx.size());
	m_iTypeDraw = iTypeDraw;
}

QOpenglGemo::QOpenglGemo(shared_ptr<QOpenglMeshObj> pMesh, QOpenGLShaderProgram* pShader, QMatrix4x4 transform)
{
	m_pMesh = pMesh;
	m_pShader = pShader;
	m_transform = transform;
	m_bIsEnableClip = false;
};

void QOpenglGemo::Draw(QOpenGLExtraFunctions *f, const QMatrix4x4& matCamera, const QMatrix4x4& matParent, const QMatrix4x4& matProj)
{
	BeginDraw(f, matCamera, matParent, matProj);
	DrawCore(f);
	EndDraw(f);
};

void QOpenglGemo::SetClipPlane(bool bEnable, QVector4D plane)
{
	m_bIsEnableClip = bEnable;
	m_vePlane = plane;
}

void QOpenglGemo::BeginDraw(QOpenGLExtraFunctions *f, const QMatrix4x4& matCamera, const QMatrix4x4& matParent, const QMatrix4x4& matProj)
{
	m_pMesh->Bind();
	m_pShader->bind();
	QMatrix4x4 matWorld = matCamera* matParent;
	m_pShader->setUniformValue("matCamera", matWorld);
	m_pShader->setUniformValue("matModelToWorld", m_transform);
	m_pShader->setUniformValue("matProj", matProj);
	m_pShader->setUniformValue("useColor", 0);
	if (m_bIsEnableClip)
	{
		glEnable(GL_CLIP_PLANE0);
		m_pShader->setUniformValue("veClipPlane", m_vePlane);
	}
	else
	{
		glDisable(GL_CLIP_PLANE0);
	}

}

void QOpenglGemo::DrawCore(QOpenGLExtraFunctions *f)
{
	int offset = 0;
	m_pShader->enableAttributeArray(0);
	m_pShader->enableAttributeArray(1);
	m_pShader->enableAttributeArray(2);


	m_pShader->setAttributeBuffer(0, GL_FLOAT, 0, 3);
	m_pShader->setAttributeBuffer(1, GL_FLOAT, m_pMesh->GetPntSize() * sizeof(QVector3D), 3);
	m_pShader->setAttributeBuffer(2, GL_FLOAT, m_pMesh->GetPntSize() * sizeof(QVector3D) * 2, 3);

	int iSize = m_pMesh->GetIndexSize();
	int iDrawType = m_pMesh->GetTypeDraw();
	f->glDrawElements(iDrawType, m_pMesh->GetIndexSize(), GL_UNSIGNED_INT, 0);

	m_pShader->disableAttributeArray(0);
	m_pShader->disableAttributeArray(1);
	m_pShader->disableAttributeArray(2);
}

void QOpenglGemo::EndDraw(QOpenGLExtraFunctions *f)
{
	m_pShader->release();
	m_pMesh->UnBind();
	if (m_bIsEnableClip)
	{
		glDisable(GL_CLIP_PLANE0);
	}
}


/////////////////////////////////////////////////////////////////////////////////////
QString QOpenglFromOpengl::GetFileData(const QString& strFileName) const
{
	QString ret;
	QFile file(strFileName);
	file.open(QFile::ReadOnly);
	if (file.isOpen())
	{
		QByteArray data = file.readAll();
		ret = data;
	}
	return ret;
}


void QOpenglFromOpengl::initializeGL()
{
	initializeOpenGLFunctions();
	const GLubyte* pVer = glGetString(GL_VERSION);

	QString nameVs = ":/GlassesParamRecognition/vert.vs", namePs = ":/GlassesParamRecognition/color.fs";
	//nameVs = "D:\\code\\SLACode\\trunk\\SLA\\R60\\R60\\R60\\vert.vs";
	//namePs = "D:\\code\\SLACode\\trunk\\SLA\\R60\\R60\\R60\\color.fs";
	m_pProgram = new QOpenGLShaderProgram(this);
	m_pProgram->addShaderFromSourceCode(QOpenGLShader::Vertex, GetFileData(nameVs));
	m_pProgram->addShaderFromSourceCode(QOpenGLShader::Fragment, GetFileData(namePs));
	m_pProgram->link();

	m_camera.ReSet();
	//LoadBufferData();
}

void QOpenglFromOpengl::resizeGL(int width, int height)
{
	m_proj.setToIdentity();
	m_proj.perspective(45.0f, GLfloat(width) / height, 0.01f, 500.0f);


}

void QOpenglFromOpengl::Draw(GLenum mode)
{
	QOpenGLExtraFunctions *f = QOpenGLContext::currentContext()->extraFunctions();
	glClearColor(155 / 255.0f, 155 / 255.0f, 1, 0);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glEnable(GL_DEPTH_TEST);
	glDisable(GL_CULL_FACE);
	glEnable(GL_MULTISAMPLE);
	//static double d = 1.3;
	//LayerAdd(d, d);
	m_MutexLock.lock();
	if (m_vGemoObj.size() > 0)
	{
		for (int i = 0; i < m_vGemoObj.size(); ++i)
		{
			if (mode == GL_SELECT)	glLoadName(i);
			pair<int, shared_ptr<QOpenglGemo>>& pGemo = m_vGemoObj[i];
			QMatrix4x4 matparent;
			matparent.setToIdentity();
			pGemo.second->Draw(f, m_camera.GetCameraMatrix(), matparent, m_proj);
		}
	}
	m_MutexLock.unlock();
}

void QOpenglFromOpengl::paintGL()
{
	Draw(GL_RENDER);
	update();
}

void QOpenglFromOpengl::keyPressEvent(QKeyEvent * e)
{
	if (!m_isCanMove) return;
	QMatrix4x4 matTrans;
	matTrans.setToIdentity();
	switch (e->key())
	{
	case Qt::Key_W:
	case Qt::Key_Up:
		m_camera.MoveInOut(true);
		break;
	case Qt::Key_S:
	case Qt::Key_Down:
		m_camera.MoveInOut(false);
		break;
	case Qt::Key_A:
	case Qt::Key_Right:
		m_camera.MoveLeftRight(false);
		break;
	case Qt::Key_D:
	case Qt::Key_Left:
		m_camera.MoveLeftRight(true);
		break;
	case Qt::Key_N:
		m_camera.SetSpeed(m_camera.GetSpeed() * 0.9);
		break;
	case Qt::Key_M:
		m_camera.SetSpeed(m_camera.GetSpeed() * 1.1);
		break;
	}
}

void QOpenglFromOpengl::mouseReleaseEvent(QMouseEvent *e)
{
	if (!m_isCanMove) return;
	Qt::MouseButtons buttons = e->buttons();
}

void QOpenglFromOpengl::mouseMoveEvent(QMouseEvent *e)
{
	if (!m_isCanMove) return;
	Qt::MouseButtons buttons = e->buttons();
	const float DeltaDistance = 0.005f;
	QMatrix4x4 matTrans;
	matTrans.setToIdentity();
	QPoint pntTo = e->pos();
	if (buttons.testFlag(Qt::MouseButton::LeftButton))
	{
		m_camera.MoveLeftRight(true, (pntTo.x() - m_pntFrom.x())*DeltaDistance, OpenGLCamera::MOVE_WITH_DISTANCE);
		m_camera.MoveUpDown(true, (pntTo.y() - m_pntFrom.y())*DeltaDistance, OpenGLCamera::MOVE_WITH_DISTANCE);
		m_pntFrom = pntTo;
	}
	if (buttons.testFlag(Qt::MouseButton::MiddleButton))
	{
		float fAngleUp = (float)((pntTo.x() - m_pntFrom.x()) * 2.0f * 3.1415f / this->width());
		float fAngleLook = (float)((pntTo.y() - m_pntFrom.y()) * 2.0f * 3.1415f / this->height());
		m_camera.Rotate(fAngleLook*0.5f, fAngleUp*0.5f);
		m_pntFrom = pntTo;
	}
}

void QOpenglFromOpengl::mouseDoubleClickEvent(QMouseEvent* e)
{
	if (!m_isCanMove) return;
	Qt::MouseButtons buttons = e->buttons();
	if (buttons.testFlag(Qt::MouseButton::LeftButton) == true)
	{
		SelectObject(e->pos().x(), e->pos().y());
	}
}

void QOpenglFromOpengl::SelectObject(int x, int y)
{

}

void QOpenglFromOpengl::mousePressEvent(QMouseEvent *e)
{

	if (!m_isCanMove) return;
	Qt::MouseButtons buttons = e->buttons();
	if (buttons.testFlag(Qt::MouseButton::LeftButton) == true ||
		buttons.testFlag(Qt::MouseButton::RightButton) == true ||
		buttons.testFlag(Qt::MouseButton::MiddleButton) == true
		)
	{
		m_pntFrom = e->pos();
	}
	//if (buttons.testFlag(Qt::MouseButton::RightButton))
	//{
	//	m_camera.ReSet();
	//}
}

void QOpenglFromOpengl::ClearAll()
{
	m_MutexLock.lock();
	m_vGemoObj.clear();
	m_vMeshObj.clear();
	m_MutexLock.unlock();
}

void QOpenglFromOpengl::AddGeneralMesh(QString strMeshName, const QVector<QVector3D>& vPnt, const QVector<QVector3D>& vNormal, const QVector<QVector3D>& vColor, const QVector<int>& vInx, int iTypeDraw)
{
	shared_ptr<QOpenglMeshObj> pMesh = shared_ptr<QOpenglMeshObj>(new QOpenglMeshObj());
	m_vMeshObj[strMeshName] = pMesh;
	pMesh->LoadGeneralObj(vPnt, vNormal, vColor, vInx, iTypeDraw);
}

void QOpenglFromOpengl::AddGemo(int gemoType, QString strMeshName, QMatrix4x4 transform, QVector3D fColor, int iUseColor)
{
	shared_ptr<QOpenglGemo> pGemo;
	if (iUseColor == 1)
	{
		pGemo = shared_ptr<QOpenglGemo>(new QOpenglColorGemo(m_vMeshObj[strMeshName], fColor, m_pProgram, transform));
	}
	else
	{
		pGemo = shared_ptr<QOpenglGemo>(new QOpenglGemo(m_vMeshObj[strMeshName], m_pProgram, transform));
	}
	m_MutexLock.lock();
	m_vGemoObj.push_back(make_pair(gemoType, pGemo));
	m_MutexLock.unlock();
}

/////////////////////////////////////////////////////////////////////////////////////////////
